﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace BlinkyDevice
{
    /// <summary>
    /// Holds the current state of the lights
    /// </summary>
    public class LightStates
    {
        /// <summary>True if red is lit up</summary>
        public bool red;
        /// <summary>True if green is lit up</summary>
        public bool green;
    }
    
    /// <summary>
    /// Arguments for button changed event
    /// </summary>
    public class BlinkyLightChangedEventArgs : EventArgs
    {
        /// <summary>Current states of the lights</summary>
        public readonly LightStates Lights;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="bStates">Current state of the lights</param>
        public BlinkyLightChangedEventArgs(LightStates lStates)
        {
            Lights = lStates;
        }
    }

    /// <summary>
    /// Delegate for the light changed event
    /// </summary>
    /// <param name="sender"></param>
    /// <param name="args"></param>
    public delegate void BlinkyLightChangedEventHandler(object sender, BlinkyLightChangedEventArgs args);

    /// <summary>
    /// Represents the USB hardware device
    /// </summary>
    class Blinky : HID
    {
        /// <summary>
        /// Event occurs whenever a light state changes
        /// </summary>
        public event BlinkyLightChangedEventHandler OnLightChanged;

        /// <summary>
        /// Creates an input report to send to the USB device
        /// </summary>
        /// <returns>Input report for the device</returns>
        public override InputReport CreateInputReport()
        {
            return new BlinkyInputReport(this);
        }

        /// <summary>
        /// Set the state of the device lights on or off
        /// </summary>
        /// <param name="red">Turn red light on or off</param>
        /// <param name="green">Turn green light on or off</param>
        public void setLights(byte state)
        {
            // Create output report
            BlinkyOutputReport oReport = new BlinkyOutputReport(this);
            // Set the light states
            oReport.setLightStates(state);
            try
            {
                // Write the output report
                Write(oReport);
            }
            catch
            {
                // exception stating the USB device may have been removed?
            }
        }

        /// <summary>
        /// Finds the Blinky device
        /// </summary>
        /// <returns>A Blinky or null if no device found</returns>
        public static Blinky findBlinkyDevice()
        {
            return (Blinky)FindDevice(4292, 0256, typeof(Blinky));
        }

        /// <summary>
        /// Occurs when data is received from the device
        /// </summary>
        /// <param name="inRep">Input report received from the device</param>
        protected override void HandleDataReceived(InputReport inRep)
        {
            // If the event handler is assigned, trigger it
            if (OnLightChanged != null)
            {
                BlinkyInputReport blinkyIn = (BlinkyInputReport)inRep;
                OnLightChanged(this, new BlinkyLightChangedEventArgs(blinkyIn.lights));
            }
        }

        /// <summary>
        /// Dispose
        /// </summary>
        /// <param name="disposing">If the object is being disposed, then true</param>
        protected override void Dispose(bool disposing)
        {
            if (disposing)
            {
                // reset the lights
                setLights(0);
            }
            base.Dispose(disposing);
        }
    }

    /// <summary>
    /// Output report for the USB device
    /// </summary>
    public class BlinkyOutputReport : OutputReport
    {
        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="dev">The device creating this report</param>
        public BlinkyOutputReport(HID dev)
            : base(dev)
        {

        }

        /// <summary>
        /// Set the state of the lights
        /// </summary>
        /// <param name="red">State of the red light</param>
        /// <param name="green">State of the green light</param>
        public void setLightStates(byte state)
        {
            byte[] byteArr = buffer;
            // report ID
            byteArr[0] = 2;
            // Currently, 1 = red, 2 = green, 3 = both
            byteArr[1] = state;
            // Currently, firmware only allows reports of 2 byte length
            //byteArr[2] = (byte)(green ? 1 : 0);
        }
    }

    /// <summary>
    /// Input report for the USB device
    /// </summary>
    public class BlinkyInputReport : InputReport
    {
        /// <summary>Current button states</summary>
        private LightStates lStates;

        /// <summary>
        /// Constructor
        /// </summary>
        /// <param name="dev">The device creating this report</param>
        public BlinkyInputReport(HID dev)
            : base(dev)
        {
        }

        /// <summary>
        /// Accessor for current light states
        /// </summary>
        public LightStates lights
        {
            get
            {
                return lStates;
            }
        }

        /// <summary>
        /// Process the raw byte data from the input report
        /// </summary>
        public override void ProcessData()
        {
            byte[] byteArr = buffer;
            lStates.red = (byteArr[1] > 0 ? true : false);
            lStates.green = (byteArr[2] > 0 ? true : false);
        }
    }
}
